X680x0アセンブラ講座 ~番外編~ 《命令のクロック数を計るプログラム》 鎌田 誠
 Motorola のマニュアルに書いてある命令のクロック数を検証するには、自分 でプログラムを書いて実際に所要時間を調べるのが一番確実です。その具体例を 紹介します。
_optime
● _optime とは  サブルーチン _optime は、指定されたサブルーチンを呼び出して帰ってくる までの時間を計るプログラムです。タイマーを 2 本使って 1μs(0.000001 秒) 単位で最大 12799μs(0.012799 秒)まで計ることができます。これを越えると 正しい結果が得られません。  10MHz の 1 クロックは 0.1μs なので、命令 1 個の時間を直接計ることはで きません。12799μs 以内でなるべく多く繰り返して、全体の所要時間を繰り返 した回数で割ることで 1 回あたりの所要時間を求めます。なお、結果には命令 を繰り返すために使用される分岐命令の所要時間も含まれます。また、呼び出し に必要な jsr/rts の所要時間も含まれるので、なるべく多く繰り返した方が正 確な値が求まります。 ● _optime の呼び出し方  _optime は Timer-C と Timer-D を使います。これらのタイマーを標準以外の 目的で使用している場合は正常に動作しません。  _optime は必ずスーパーバイザモードで呼び出さなければなりません。  _optime の呼び出し方は次のようになっています。 ┌──────────────────────────────── │ move.l #count,-(sp) ;初期化ルーチンのパラメータ(ループ回数など) │ pea.l op_tini ;後始末ルーチン(不要ならば0) │ pea.l op_init ;初期化ルーチン(不要ならば0) │ pea.l op_main ;計測するルーチン │ jsr optime │ lea.l (16,sp),sp 次のような前宣言をすることで C のプログラムから呼び出すこともできます。 ┌──────────────────────────────── │int optime(void op_main(void), void op_init(int count), void op_tini(void), int count); ● _optime の動作  _optime は、op_init(初期化ルーチン)、op_main(計測するルーチン)、 op_tini(後始末ルーチン)の順序で 2 回呼び出し、2 回目の op_main の所要 時間だけ計測します。2 回呼び出すのは、計測ルーチンをキャッシュに乗せるた めです。一連の呼び出し(6 回のサブルーチンコール)は割り込みを禁止した状 態で行われます。 ●レジスタの扱いについて  op_init では op_main で使用するレジスタの準備をして下さい。optime の 4 番目の引数(count)が op_init に渡されるので、これをレジスタに設定するな どします。メモリを確保するといった大掛かりな処理も可能ですが、割り込みが 禁止されているのでディスクのアクセスなどはできません。op_init で設定され たレジスタは d0-d7/a0-a5 の内容がそのまま op_main に渡されます。  op_main では d0-d7/a0-a6 をすべて使えます。ただし、_optime が op_main を呼び出すときに jsr (a6) として a6 レジスタを使用しているので、a6 レジ スタは op_init から引き継ぐことができません。  op_tini は後始末です。通常は不要でしょう。op_init でメモリを確保したと きは必ずここで開放して下さい。 ◎ optime.s を見る
メインルーチン
 _optime はサブルーチンなので、これを実行するにはメインルーチンが必要で す。  計測対象のルーチンを _op_init、_op_main、_op_tini という名前で呼び出す メインルーチンを用意しました。コマンドラインでループ回数(op_init の引数) を指定することができます。このソースだけ C で書いてあります。 ◎ main.c を見る
具体例(その1)
 NOP+SUBQ.L #xx,Dn+Bcc.S の所要クロック数を調べます。  NOP は 68000 では 4 クロック、68030 では 2 クロック、68060 では 9 クロ ックの命令です。  SUBQ.L #xx,Dn は 68000 では 8 クロック、68030 では 2 クロック、68060 では 1 クロックの命令です。  Bcc.S は 68000 では 10 クロック、68030 では 6 クロック、68060 では 0 クロック(分岐予測あり)です。 ●ソース ┌──────────────────────────────── 1│;nop+subq.l+bcc.s 2│ .text 3│ .even 4│_op_init:: 5│ move.l (4,sp),d0 6│ rts 7│ 8│ .even 9│_op_tini:: 10│ rts 11│ 12│ .align 16 13│_op_main:: 14│ ; 000 030 060 15│@@: nop ; 4 0-2-0 9 16│ subq.l #1,d0 ; 8 2-0-0 1 17│ bne.s @b ; 10 6-0-0 0 18│ ; =22 =10 =10 19│ rts 20│ 21│_op_name:: 22│ .dc.b 'nop',0 ●結果 ・X68000XVI 10MHz での結果 [nop] total=11313(μs) count=5000 each=2.2626(μs) each=22.626(clock/10MHz) ・X68000XVI 16MHz での結果 [nop] total=10694(μs) count=8000 each=1.3368(μs) each=22.2796(clock/16.667MHz) ・X68000XVI 24MHz(REDZONE)での結果 [nop] total=9191(μs) count=10000 each=0.9191(μs) each=22.0584(clock/24MHz) ・X68030 での結果 [nop] total=8000(μs) count=20000 each=0.4(μs) each=10(clock/25MHz) ・X68030+060turbo での結果 [nop] total=10000(μs) count=50000 each=0.2(μs) each=10(clock/50MHz) ●補足  68060 のクロック数が多いのは、NOP 命令がパイプラインを掃除するための命 令だからです。コードにパッチを当てるときに隙間を埋めるためには MOVEA.L A0,A0 を使います。
具体例(その2)
 MULU.W Dn,Dn+SUBQ.L #xx,Dn+Bcc.S の所要クロック数を調べます。  MULU.W Dn,Dn は 68000 では 38~70 クロック、68030 では 28 クロック、 68060 では 2 クロックの命令です。これを確認します。68000 のクロック数は ソースオペランドに含まれる 1 のビットの数に比例しますが、ここではワース トケースの $FFFF で計っています。  MULU.W Dn,Dn 1 回の所要時間が X68000XVI 10MHz で 7μs、060turbo で 0.04μs ですから、ワーストケースで所要時間が 175 倍も違うことがわかりま す。 ●ソース ┌──────────────────────────────── 1│;mulu+subq.l+bcc.s 2│ .text 3│ .even 4│_op_init:: 5│ move.l (4,sp),d0 6│ move.w #$FFFF,d1 ;worst case 7│ rts 8│ 9│ .even 10│_op_tini:: 11│ rts 12│ 13│ .align 16 14│_op_main:: 15│ ; 000 030 060 16│@@: move.w d1,d2 ; 4 2-0-0 1 17│ mulu.w d2,d2 ; 70 2-26-0 2 18│ subq.l #1,d0 ; 8 2-0-0 1 19│ bne.s @b ; 10 6-0-0 0 20│ ; =92 =38 =4 21│ rts 22│ 23│_op_name:: 24│ .dc.b 'mulu',0 ●結果 ・X68000XVI 10MHz での結果 [mulu] total=9245(μs) count=1000 each=9.245(μs) each=92.45(clock/10MHz) ・X68000XVI 16MHz での結果 [mulu] total=11063(μs) count=2000 each=5.5315(μs) each=92.1935(clock/16.667MHz) ・X68000XVI 24MHz(REDZONE)での結果 [mulu] total=11529(μs) count=3000 each=3.843(μs) each=92.232(clock/24MHz) ・X68030 での結果 [mulu] total=12161(μs) count=8000 each=1.5201(μs) each=38.0031(clock/25MHz) ・X68030+060turbo での結果 [mulu] total=12000(μs) count=150000 each=0.08(μs) each=4(clock/50MHz) (EOF)